﻿#ifdef UNICODE
#else
#define UNICODE
#endif

#include <windows.h>
#include <stdio.h>
#include <crtdbg.h>
#include "jni.h"
#include <mbstring.h>
#include <Psapi.h>
#define NUM_ITEMS(X) (sizeof(X)/sizeof((X)[0]))
HINSTANCE mHinst = 0, mHinstDLL = 0;
#pragma pack(1)

LPCSTR mImportNames[] = {
	"?Java_com_ir_1rc_shared_etc_Windows_nativeIsAlreadyRunning@@YGEPAUJNIEnv_@@PAV_jclass@@@Z",
	"?Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeSetHint@@YGXPAUJNIEnv_@@PAV_jobject@@PAV_jbyteArray@@@Z",
	"?Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeSetIcon@@YGXPAUJNIEnv_@@PAV_jobject@@J@Z",
	"_Java_com_ir_1rc_shared_etc_Windows_checkApplicationSynonim@12",
	"_Java_com_ir_1rc_shared_etc_Windows_enumCommPorts@8",
	"_Java_com_ir_1rc_shared_etc_Windows_getCurrentProcessId@8",
	"_Java_com_ir_1rc_shared_etc_Windows_getCurrentlyPressedKey@8",
	"_Java_com_ir_1rc_shared_etc_Windows_getKeyName@12",
	"_Java_com_ir_1rc_shared_etc_Windows_getRegistryKeyForExtension@12",
	"_Java_com_ir_1rc_shared_etc_Windows_getScreenWidth@8",
	"_Java_com_ir_1rc_shared_etc_Windows_moveCursor@24",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeActivateApp@24",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeCheckStartUp@20",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeCloseProcessHandle@12",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeEnumProcesses@8",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeGetChildWindowClass@12",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeGetDriveType@12",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeGetMouseLocation@8",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeGetWin32ProcessHandle@24",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeGetWindowFilePath@12",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeIsWindowFullscreen@12",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeIshProcessValid@12",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeKeybdEvent@32",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeLaunchApp@24",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeMouse_1dragged@12",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeMouse_1up@8",
	"_Java_com_ir_1rc_shared_etc_Windows_nativePerformAction@12",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeRestoreGlobalCursor@8",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeSendSystemMessage@48",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeSetGlobalCursorToBig@8",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeShutDownProcess@12",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeTerminateApp@12",
	"_Java_com_ir_1rc_shared_etc_Windows_nativeVolumeCommand@12",
	"_Java_com_ir_1rc_shared_etc_Windows_pressKey@12",
	"_Java_com_ir_1rc_shared_etc_Windows_pressMouseKey@12",
	"_Java_com_ir_1rc_shared_etc_Windows_releaseKey@12",
	"_Java_com_ir_1rc_shared_etc_Windows_releaseMouseKey@12",
	"_Java_com_ir_1rc_shared_etc_Windows_resetActivationMonitor@8",
	"_Java_com_ir_1rc_shared_etc_Windows_startActivationMonitor@12",
	"_Java_com_ir_1rc_shared_etc_Windows_startWatching@12",
	"_Java_com_ir_1rc_shared_etc_Windows_stopActivationMonitor@8",
	"_Java_com_ir_1rc_shared_etc_Windows_stopWatching@8",
	"_Java_com_ir_1rc_shared_gui_signalWindow_SignalWindow_nativeHide@8",
	"_Java_com_ir_1rc_shared_gui_signalWindow_SignalWindow_nativeShow@12",
	"_Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeAddMenuItem@16",
	"_Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeCheckAlreadyRunning@8",
	"_Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeDestroy@8",
	"_Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeInit@8",
	"_Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeLoadImage@12",
	"_Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeShow@16"
};
FARPROC mProcs[NUM_ITEMS(mImportNames)] = {0};

BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) {
	mHinst = hinstDLL;
	if ( fdwReason == DLL_PROCESS_ATTACH ) {
		if (!(mHinstDLL = LoadLibrary( L"windows1.dll"))) {
			return false;
		}
		for ( int i = 0; i < NUM_ITEMS(mImportNames); i++ )
			mProcs[ i ] = GetProcAddress( mHinstDLL, mImportNames[ i ] );
	} else if ( fdwReason == DLL_PROCESS_DETACH ) {
		FreeLibrary( mHinstDLL );
	}
	return true;
}


extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeIsAlreadyRunning__YGEPAUJNIEnv___PAV_jslass___Z(){__asm{jmp mProcs[0*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeIsAlreadyRunning__YGEPAUJNIEnv___PAV_jclass___Z(){__asm{jmp mProcs[0*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeSetHint__YGXPAUJNIEnv___PAV_jobject__PAV_jbyteArray___Z(){__asm{jmp mProcs[1*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeSetIcon__YGXPAUJNIEnv___PAV_jobject__J_Z(){__asm{jmp mProcs[2*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_checkApplicationSynonim_12(){__asm{jmp mProcs[3*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_enumCommPorts_8(){__asm{jmp mProcs[4*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_getCurrentProcessId_8(){__asm{jmp mProcs[5*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_getCurrentlyPressedKey_8(){__asm{jmp mProcs[6*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_getKeyName_12(){__asm{jmp mProcs[7*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_getRegistryKeyForExtension_12(){__asm{jmp mProcs[8*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_getScreenWidth_8(){__asm{jmp mProcs[9*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_moveCursor_24(){__asm{jmp mProcs[10*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeActivateApp_24(){__asm{jmp mProcs[11*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeCheckStartUp_20(){__asm{jmp mProcs[12*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeCloseProcessHandle_12(){__asm{jmp mProcs[13*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeEnumProcesses_8(){__asm{jmp mProcs[14*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeGetChildWindowClass_12(){__asm{jmp mProcs[15*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeGetDriveType_12(){__asm{jmp mProcs[16*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeGetMouseLocation_8(){__asm{jmp mProcs[17*4]}}
//extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeGetWin32ProcessHandle_24(){__asm{jmp mProcs[18*4]}}
//extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeGetWindowFilePath_12(){__asm{jmp mProcs[19*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeIsWindowFullscreen_12(){__asm{jmp mProcs[20*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeIshProcessValid_12(){__asm{jmp mProcs[21*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeKeybdEvent_32(){__asm{jmp mProcs[22*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeLaunchApp_24(){__asm{jmp mProcs[23*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeMouse_1dragged_12(){__asm{jmp mProcs[24*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeMouse_1up_8(){__asm{jmp mProcs[25*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativePerformAction_12(){__asm{jmp mProcs[26*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeRestoreGlobalCursor_8(){__asm{jmp mProcs[27*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeSendSystemMessage_48(){__asm{jmp mProcs[28*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeSetGlobalCursorToBig_8(){__asm{jmp mProcs[29*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeShutDownProcess_12(){__asm{jmp mProcs[30*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeTerminateApp_12(){__asm{jmp mProcs[31*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_nativeVolumeCommand_12(){__asm{jmp mProcs[32*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_pressKey_12(){__asm{jmp mProcs[33*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_pressMouseKey_12(){__asm{jmp mProcs[34*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_releaseKey_12(){__asm{jmp mProcs[35*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_releaseMouseKey_12(){__asm{jmp mProcs[36*4]}}
//extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_resetActivationMonitor_8(){__asm{jmp mProcs[37*4]}}
//extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_startActivationMonitor_12(){__asm{jmp mProcs[38*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_startWatching_12(){__asm{jmp mProcs[39*4]}}
//extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_stopActivationMonitor_8(){__asm{jmp mProcs[40*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_etc_Windows_stopWatching_8(){__asm{jmp mProcs[41*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_gui_signalWindow_SignalWindow_nativeHide_8(){__asm{jmp mProcs[42*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_gui_signalWindow_SignalWindow_nativeShow_12(){__asm{jmp mProcs[43*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeAddMenuItem_16(){__asm{jmp mProcs[44*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeCheckAlreadyRunning_8(){__asm{jmp mProcs[45*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeDestroy_8(){__asm{jmp mProcs[46*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeInit_8(){__asm{jmp mProcs[47*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeLoadImage_12(){__asm{jmp mProcs[48*4]}}
extern "C" __declspec(naked) void __stdcall _Java_com_ir_1rc_shared_gui_sysTray_TrayIcon_nativeShow_16(){__asm{jmp mProcs[49*4]}}

#define REAL_MAX_PATH 32768
void printLastError() {
    LPTSTR buffer = NULL;
	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, buffer, 0, NULL);
	if (buffer) {
		wprintf(L"Formatted message: %s\n", buffer);
		LocalFree(buffer);
	}
}
HANDLE getPIDFilePath(DWORD pid, char *buffer, size_t b_size) {
    HANDLE ph = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid);
    if (ph) {
		TCHAR filename[REAL_MAX_PATH] = {0};
		DWORD filename_size = REAL_MAX_PATH;
	    if (QueryFullProcessImageName(ph, 0, filename, &filename_size)) {
			WideCharToMultiByte(CP_UTF8, 0, filename, _countof(filename), buffer, b_size, NULL, NULL);
	    } else {
	    	printLastError();
	    	ph = NULL;
	    }
    }
    return ph;
}
/**
 * Отримання шляху до файлу процеса за хендлом вікна
 * @hWnd - хендл вікна
 * @buffer - буфер під utf8 рядок
 * @b_size - розмір буферу
 * return - boolean (true on success)
 */
bool getWindowFilePath(HWND hWnd, char *buffer, size_t b_size) {
    DWORD pid = NULL;
    GetWindowThreadProcessId((HWND)hWnd, &pid);
    return getPIDFilePath(pid, buffer, b_size) != NULL;
}


/**
 * Отримання шляху до файлу процеса за хендлом вікна (JNI)
 * @h - хендл вікна
 * return - jstring
 */
extern "C" JNIEXPORT jstring JNICALL Java_com_ir_1rc_shared_etc_Windows_nativeGetWindowFilePath(JNIEnv *env, jobject obj, jint h){
    char filename[REAL_MAX_PATH] = {0};

    if (!getWindowFilePath((HWND)h, filename, sizeof(filename))) {
    	filename[0] = '\0';
    }

	return env->NewStringUTF(filename);
}


int ActivationMonitorFlag = 0;

/**
 * Запуск моніторингу вікон (JNI)
 * @h - хендл вікна
 * return - jstring
 */
extern "C" JNIEXPORT jstring JNICALL Java_com_ir_1rc_shared_etc_Windows_startActivationMonitor(JNIEnv *env, jobject obj, jobjectArray in_array){
    char ret_filename[REAL_MAX_PATH] = {0};
    int cnt = env->GetArrayLength(in_array);
	const char **names = (const char **)calloc(cnt, sizeof(char *));

	printf("Start monitoring\r");
	for (int i=0; i<cnt; i++) {
		names[i] = env->GetStringUTFChars((jstring)env->GetObjectArrayElement(in_array, i), 0);
		printf("%s\n", names[i]);
	}
	while (ActivationMonitorFlag == 0 && ret_filename[0] == '\0') {
	    char fore_filename[REAL_MAX_PATH] = {0};

		if (getWindowFilePath(GetForegroundWindow(), fore_filename, sizeof(fore_filename))) {
			for(int i=0; i<cnt; i++) {
				if (!_mbscmp((const unsigned char*)fore_filename, (const unsigned char*)names[i])) {
					printf("Catch: %s\r", names[i]);
					strcpy_s(ret_filename, sizeof(ret_filename), fore_filename);
					break;
				}
			}
		}
		Sleep(100);
	}
	for (int i=0; i<cnt; i++) {
		env->ReleaseStringUTFChars((jstring)env->GetObjectArrayElement(in_array, i), names[i]);
	}
	return env->NewStringUTF(ret_filename);
}

/**
 * Зупинка моніторингу вікон (JNI)
 * @h - хендл вікна
 */
typedef void (WINAPI *stopActivationMonitor)(JNIEnv *, jobject);
extern "C" JNIEXPORT void JNICALL Java_com_ir_1rc_shared_etc_Windows_stopActivationMonitor(JNIEnv *env, jobject obj){
	((stopActivationMonitor)(mProcs[40]))(env, obj);
	ActivationMonitorFlag = 1;
}

/**
 * Обнулення моніторингу вікон (JNI)
 * @h - хендл вікна
 */
extern "C" JNIEXPORT void JNICALL Java_com_ir_1rc_shared_etc_Windows_resetActivationMonitor(JNIEnv *env, jobject obj){
	ActivationMonitorFlag = 0;
}

/**
 * Обнулення моніторингу вікон (JNI)
 * @h - хендл вікна
 * return - jstring
 */
typedef jint (WINAPI *nativeGetWin32ProcessHandle)(JNIEnv *, jobject, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
#define PIDS_MAX 16384
extern "C" JNIEXPORT jint JNICALL Java_com_ir_1rc_shared_etc_Windows_nativeGetWin32ProcessHandle(JNIEnv *env, jobject obj, jbyteArray in_path, jbyteArray in_caption, jbyteArray in_class, jbyteArray in_type){
    jboolean isCopy;
    jbyte *b;
    HANDLE ret_handle = NULL;

	if (in_path) {
	    const unsigned char srch[REAL_MAX_PATH] = {0};
		b = env->GetByteArrayElements(in_path, &isCopy);
		strncpy_s((char *)srch, _countof(srch), (char *)b, env->GetArrayLength(in_path));
		env->ReleaseByteArrayElements(in_path, b, JNI_ABORT);
		DWORD *pids = (DWORD *)calloc(PIDS_MAX, sizeof(DWORD));
		DWORD pid_count = 0;
		if (EnumProcesses(pids, PIDS_MAX*sizeof(DWORD), &pid_count)) {
			char filename[REAL_MAX_PATH] = {0};
			for (UINT i=0; i<pid_count/sizeof(DWORD); i++) {
				ret_handle = getPIDFilePath(pids[i], filename, sizeof(filename));
				if (ret_handle) {
					if (!_mbscmp(srch, (const unsigned char*)filename)) {
						break;
					} else {
						ret_handle = NULL;
					}
				}
			}
		}
		free(pids);
	} else {
		return ((nativeGetWin32ProcessHandle)(mProcs[18]))(env, obj, in_path, in_caption, in_class, in_type);
	}
	return (jint)ret_handle;
}
